home *** CD-ROM | disk | FTP | other *** search
/ InterCD 2001 May / may_2001.iso / intercd / root / Multimedia / ^DivX_Article / virtualdub / VirtualDub-source-1_4d / AVIAudioOutput.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2001-03-20  |  8.2 KB  |  376 lines

  1. //    VirtualDub - Video processing and capture application
  2. //    Copyright (C) 1998-2001 Avery Lee
  3. //
  4. //    This program is free software; you can redistribute it and/or modify
  5. //    it under the terms of the GNU General Public License as published by
  6. //    the Free Software Foundation; either version 2 of the License, or
  7. //    (at your option) any later version.
  8. //
  9. //    This program is distributed in the hope that it will be useful,
  10. //    but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. //    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12. //    GNU General Public License for more details.
  13. //
  14. //    You should have received a copy of the GNU General Public License
  15. //    along with this program; if not, write to the Free Software
  16. //    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  17.  
  18. #include <string.h>
  19. #include <stdio.h>
  20. #include <crtdbg.h>
  21.  
  22. #include <windows.h>
  23. #include <mmsystem.h>
  24.  
  25. #include "AVIAudioOutput.h"
  26.  
  27. AVIAudioOutputBuffer::AVIAudioOutputBuffer(long bsize) {
  28.     memset(&hdr, 0, sizeof hdr);
  29.     hdr.lpData            = new char[bsize];
  30.     hdr.dwBufferLength    = bsize;
  31.     next = prev = NULL;
  32.     dwBytesInBuffer = 0;
  33.  
  34. }
  35.  
  36. AVIAudioOutputBuffer::~AVIAudioOutputBuffer() {
  37.     delete hdr.lpData;
  38. }
  39.  
  40. BOOL AVIAudioOutputBuffer::init(HWAVEOUT hWaveOut) {
  41.     return MMSYSERR_NOERROR == waveOutPrepareHeader(hWaveOut,&hdr,sizeof(WAVEHDR));
  42. }
  43.  
  44. BOOL AVIAudioOutputBuffer::post(HWAVEOUT hWaveOut) {
  45.     return MMSYSERR_NOERROR == waveOutWrite(hWaveOut,&hdr,sizeof(WAVEHDR));
  46. }
  47.  
  48. void AVIAudioOutputBuffer::deinit(HWAVEOUT hWaveOut) {
  49.     waveOutUnprepareHeader(hWaveOut, &hdr, sizeof(WAVEHDR));
  50. }
  51.  
  52. AVIAudioOutput::AVIAudioOutput(long bufsize, int maxbufs) {
  53.     this->maxbufs        = maxbufs;
  54.     this->bufsize        = bufsize;
  55.  
  56.     numbufs = 0;
  57.     pending = pending_tail = active = NULL;
  58.     curState = STATE_NONE;
  59.  
  60.     fill_byte = (char)0x00;
  61.     iBuffersActive = 0;
  62.  
  63.     hEventBuffersFree    = CreateEvent(NULL,FALSE,FALSE,NULL);
  64. }
  65.  
  66. AVIAudioOutput::~AVIAudioOutput() {
  67.     AVIAudioOutputBuffer *nb;
  68.  
  69.     if (curState == STATE_SILENT) return;
  70.  
  71.     stop();
  72.  
  73.     while(nb = pending) {
  74.         pending = pending->next;
  75.         nb->deinit(hWaveOut);
  76.         delete nb;
  77.     }
  78.  
  79.     while(nb = active) {
  80.         active = active->next;
  81.         nb->deinit(hWaveOut);
  82.         delete nb;
  83.     }
  84.  
  85.     if (curState >= STATE_OPENED)
  86.         waveOutClose(hWaveOut);
  87.  
  88.     if (hEventBuffersFree)
  89.         CloseHandle(hEventBuffersFree);
  90. }
  91.  
  92. BOOL AVIAudioOutput::init(WAVEFORMATEX *wf) {
  93.     MMRESULT res;
  94.     AVIAudioOutputBuffer *aaob;
  95.  
  96.     if (wf->wFormatTag == WAVE_FORMAT_PCM)
  97.         if (wf->wBitsPerSample == 8)
  98.             fill_byte = (char)0x80;
  99.  
  100.     if (MMSYSERR_NOERROR != (res = waveOutOpen(&hWaveOut, WAVE_MAPPER, wf, (DWORD)hEventBuffersFree, 0, CALLBACK_EVENT)))
  101.         return FALSE;
  102.  
  103.     curState = STATE_OPENED;
  104.     nSamplesPerSec = wf->nSamplesPerSec;
  105.     nAvgBytesPerSec = wf->nAvgBytesPerSec;
  106.  
  107.     // Hmmm... we can't allocate buffers while the wave device
  108.     // is active...
  109.  
  110.     while(numbufs < maxbufs) {
  111.         aaob = new AVIAudioOutputBuffer(bufsize);
  112.         if (!aaob) break;
  113.  
  114.         if (!aaob->init(hWaveOut)) {
  115.             _RPT0(0,"Init failed\n");
  116.             delete aaob;
  117.             return FALSE;
  118.         }
  119.  
  120. #if 0
  121.         {
  122.             char buf[128];
  123.             wsprintf(buf, "Allocated audio buffer of %ld bytes\n", aaob->hdr.dwBufferLength);
  124.             OutputDebugString(buf);
  125.         }
  126. #endif
  127.  
  128.         aaob->prev = NULL;
  129.         aaob->next = pending;
  130.         if (pending) pending->prev = aaob;
  131.         else pending_tail = aaob;
  132.         pending = aaob;
  133.  
  134.         ++numbufs;
  135.     }
  136.  
  137.     lAvailSpace = numbufs * bufsize;
  138.  
  139.     waveOutPause(hWaveOut);
  140.  
  141.     return TRUE;
  142. }
  143.  
  144. void AVIAudioOutput::go_silent() {
  145.     curState = STATE_SILENT;
  146. }
  147.  
  148. BOOL AVIAudioOutput::isSilent() {
  149.     return curState == STATE_SILENT;
  150. }
  151.  
  152. BOOL AVIAudioOutput::start() {
  153.     if (curState == STATE_SILENT) return TRUE;
  154.  
  155.     if (curState < STATE_OPENED) return FALSE;
  156.  
  157.     if (MMSYSERR_NOERROR != waveOutRestart(hWaveOut))
  158.         return FALSE;
  159.  
  160.     curState = STATE_PLAYING;
  161.  
  162.     return TRUE;
  163. }
  164.  
  165. BOOL AVIAudioOutput::stop() {
  166.     if (curState == STATE_SILENT) return TRUE;
  167.  
  168.     if (curState >= STATE_OPENED) {
  169.         if (MMSYSERR_NOERROR != waveOutReset(hWaveOut))
  170.             return FALSE;
  171.  
  172.         curState = STATE_OPENED;
  173.     }
  174.  
  175.     return TRUE;
  176. }
  177.  
  178. BOOL AVIAudioOutput::checkBuffers() {
  179.     AVIAudioOutputBuffer *aaob,*aaob2;
  180.     int found = 0;
  181.  
  182.     if (curState == STATE_SILENT) return TRUE;
  183.  
  184.     aaob = active;
  185.     while(aaob) {
  186.         if (aaob->hdr.dwFlags & WHDR_DONE) {
  187.             aaob2=aaob->next;
  188.             if (aaob->prev) aaob->prev->next = aaob->next; else active=aaob->next;
  189.             if (aaob->next) aaob->next->prev = aaob->prev;
  190.             aaob->hdr.dwFlags &= ~WHDR_DONE;
  191.             aaob->dwBytesInBuffer = 0;
  192.  
  193.             aaob->prev = NULL;
  194.             aaob->next = pending;
  195.             if (pending) pending->prev = aaob;
  196.             else pending_tail = aaob;
  197.             pending = aaob;
  198.  
  199.             --iBuffersActive;
  200.             lAvailSpace += aaob->hdr.dwBufferLength;
  201.  
  202.             aaob=aaob2;
  203.             ++found;
  204.         } else
  205.             aaob=aaob->next;
  206.     }
  207.  
  208. //    _RPT1(0,"%d buffers returned\n",found);
  209.  
  210.     return found>0;
  211. }
  212.  
  213. BOOL AVIAudioOutput::waitBuffers(DWORD timeout) {
  214.     if (curState == STATE_SILENT) return TRUE;
  215.  
  216.     if (hEventBuffersFree && timeout) {
  217.         for(;;) {
  218.             if (WAIT_OBJECT_0 != WaitForSingleObject(hEventBuffersFree, timeout))
  219.                 return FALSE;
  220.  
  221.             if (checkBuffers())
  222.                 return TRUE;
  223.         }
  224.     }
  225.  
  226.     return checkBuffers();
  227. }
  228.  
  229. long AVIAudioOutput::avail() {
  230.     checkBuffers();
  231.     return lAvailSpace;
  232. }
  233.  
  234. BOOL AVIAudioOutput::write(void *data, long len, DWORD timeout) {
  235.     AVIAudioOutputBuffer *aaob;
  236.     long tc;
  237.  
  238.     if (curState == STATE_SILENT) return TRUE;
  239.  
  240.     checkBuffers();
  241. //    _RPT1(0,"writing %ld bytes\n",len);
  242.  
  243.     while(len) {
  244.         if (!pending) {
  245.  
  246.             if (!waitBuffers(0) && timeout) {
  247.                 if (!waitBuffers(timeout)) {
  248.                     return FALSE;
  249.                 }
  250.                 continue;
  251.             }
  252.             break;
  253.         }
  254.  
  255.         aaob = pending_tail;
  256.  
  257.         tc = aaob->hdr.dwBufferLength - aaob->dwBytesInBuffer;
  258.         if (tc > len) tc=len;
  259.  
  260.         if (tc)
  261.             memcpy((char *)aaob->hdr.lpData + aaob->dwBytesInBuffer, data, tc);
  262.  
  263.         lAvailSpace -= tc;
  264.  
  265.         // if the buffer is full, ship it out
  266.  
  267.         if ((aaob->dwBytesInBuffer += tc) >= aaob->hdr.dwBufferLength) {
  268. #if 0
  269.             {
  270.                 char buf[128];
  271.                 wsprintf(buf, "Posting audio buffer of %ld/%ld bytes (%ld already active)\n", aaob->dwBytesInBuffer, aaob->hdr.dwBufferLength, iBuffersActive);
  272.                 OutputDebugString(buf);
  273.             }
  274. #endif
  275.             if (!postBuffer(aaob)) return FALSE;
  276.         }
  277.  
  278.         len -= tc;
  279.         data = (char *)data + tc;
  280.     }
  281.  
  282.     return TRUE;
  283. }
  284.  
  285. void AVIAudioOutput::flush() {
  286.     if (pending_tail && pending_tail->dwBytesInBuffer)
  287.         postBuffer(pending_tail);
  288. }
  289.  
  290. BOOL AVIAudioOutput::finalize(DWORD timeout) {
  291.     if (curState == STATE_SILENT) return TRUE;
  292.  
  293.     _RPT0(0,"AVIAudioOutput: finalizing output\n");
  294.  
  295.     flush();
  296.  
  297.     while(checkBuffers(), active)
  298.         if (hEventBuffersFree != INVALID_HANDLE_VALUE)
  299.             if (WAIT_OBJECT_0 != WaitForSingleObject(hEventBuffersFree, timeout))
  300.                 return FALSE;
  301.  
  302.     _RPT0(0,"AVIAudioOutput: finalized.\n");
  303.  
  304.     return TRUE;
  305. }
  306.  
  307. BOOL AVIAudioOutput::postBuffer(AVIAudioOutputBuffer *aaob) {
  308.     // delink from pending list
  309.  
  310.     if (aaob->prev) aaob->prev->next = aaob->next; else pending=aaob->next;
  311.     if (aaob->next) aaob->next->prev = aaob->prev; else pending_tail=aaob->prev;
  312.  
  313.     // clear rest of data
  314.  
  315.     if (aaob->dwBytesInBuffer < aaob->hdr.dwBufferLength)
  316.         memset((char *)aaob->hdr.lpData + aaob->dwBytesInBuffer, fill_byte, aaob->hdr.dwBufferLength-aaob->dwBytesInBuffer);
  317.  
  318.     // post to wave device
  319.  
  320.     aaob->hdr.dwFlags &= ~WHDR_DONE;
  321.  
  322.     if (!aaob->post(hWaveOut)) {
  323.         _RPT0(0,"post failed!\n");
  324.         return FALSE;
  325.     }
  326.  
  327.     // link to active list
  328.  
  329.     aaob->prev = NULL;
  330.     aaob->next = active;
  331.     if (active) active->prev = aaob;
  332.     active = aaob;
  333.  
  334.     ++iBuffersActive;
  335.  
  336.     return TRUE;
  337. }
  338.  
  339. long AVIAudioOutput::position() {
  340.     MMTIME mmtime;
  341.  
  342.     if (curState != STATE_PLAYING) return -1;
  343.  
  344.     mmtime.wType = TIME_SAMPLES;
  345.  
  346.     if (MMSYSERR_NOERROR != waveOutGetPosition(hWaveOut, &mmtime, sizeof mmtime))
  347.         return -1;
  348.  
  349.     switch(mmtime.wType) {
  350.     case TIME_BYTES:
  351.         return MulDiv(mmtime.u.cb, 1000, nAvgBytesPerSec);
  352.     case TIME_MS:
  353.         return mmtime.u.ms;
  354.     case TIME_SAMPLES:
  355.         return MulDiv(mmtime.u.sample, 1000, nSamplesPerSec);
  356.     }
  357.  
  358.     return -1;
  359. }
  360.  
  361. bool AVIAudioOutput::isFrozen() {
  362.     AVIAudioOutputBuffer *aaob;
  363.  
  364.     if (curState != STATE_PLAYING) return true;
  365.  
  366.     aaob = active;
  367.     while(aaob) {
  368.         if (!(aaob->hdr.dwFlags & WHDR_DONE))
  369.             return false;
  370.  
  371.         aaob=aaob->next;
  372.     }
  373.  
  374.     return true;
  375. }
  376.